package com.jiaozi.core.security.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jiaozi.base.enums.sys.StateEnum;
import com.jiaozi.base.redis.RedisEnum;
import com.jiaozi.base.redis.RedisUtil;
import com.jiaozi.core.exception.CommonException;
import com.jiaozi.core.security.pojo.login.LuDepartmentVO;
import com.jiaozi.core.security.pojo.login.LuMenuVO;
import com.jiaozi.core.security.pojo.login.LuRoleVO;
import com.jiaozi.core.security.properties.JiaoZiJwtPropertie;
import com.jiaozi.core.security.pojo.LoginUserDetails;
import com.jiaozi.base.utils.bean.R;
import com.jiaozi.base.utils.bean.enums.REnum;
import com.jiaozi.system.entity.*;
import com.jiaozi.core.security.pojo.param.LoginParam;
import com.jiaozi.system.enums.ConfigsEnum;
import com.jiaozi.system.enums.MenuTypeEnum;
import com.jiaozi.system.service.*;
import com.jiaozi.base.utils.IpUtil;
import com.jiaozi.base.utils.WebUtil;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private SysUserService sysUserService;
    @Resource
    private JiaoZiJwtPropertie jiaoziJwtPropertie;
    @Resource
    private LoginLockServiceImpl loginLockService;
    @Autowired
    private SysDepartmentService sysDepartmentService;

    @Autowired
    private SysRoleOrMenuService sysRoleOrMenuService;
    @Autowired
    private SysUserOrRoleService sysUserOrRoleService;
    @Autowired
    private SysMenuService sysMenuService;

    @Autowired
    private SysConfigsService sysConfigsService;
    @Override
    public R login(LoginParam loginParam) {

        //是否可以登录
        loginLockService.isLock(loginParam.getUserName());
        // 进行用户认证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginParam.getUserName(), loginParam.getPassword());
        Authentication authenticate = null;
        try {
            authenticate = authenticationManager.authenticate(authenticationToken);
        } catch (BadCredentialsException e) {
            //增加登录错误
            loginLockService.loginError(loginParam.getUserName());
            throw new CommonException(REnum.LOGIN_PASSWORD_ERROR,"");
        }
        //获取用户信息
        LoginUserDetails loginUserDetails = (LoginUserDetails) authenticate.getPrincipal();
        //组装部门信息
        loginUserDetails.setDepartment(getDepartment(loginUserDetails.getLoginUser().getDepartmentId()));
        if(loginUserDetails.getUsername().equals("admin")){
            //组装菜单信息
            loginUserDetails.setMenus(adminMenus());
            List<LuRoleVO> roles=new ArrayList<>();
            roles.add(new LuRoleVO("0","超级管理员"));
            loginUserDetails.setRoles(roles);
        }else{
            //组装角色信息
            loginUserDetails.setRoles(getRroles(loginUserDetails.getLoginUser().getUserId()));
            //组装菜单信息
            loginUserDetails.setMenus(roleMenus(loginUserDetails.getRoles()));
        }

        //组装权限信息
        loginUserDetails.setPermissions(permissions(loginUserDetails.getMenus()));


        String uuid = IdUtil.simpleUUID();
        // 将用户登录信息存入缓存
        redisUtil.setStringExpire(RedisEnum.LOGIN_INFO , loginUserDetails.getLoginUser().getUserId()+":"+uuid, JSONObject.toJSONString(loginUserDetails), Long.valueOf(sysConfigsService.getSysConfigs(ConfigsEnum.LOGIN_ACTIVE_EXPIRATION) ));

        // 更新用户登录信息
        sysUserService.update(new LambdaUpdateWrapper<SysUser>()
                .eq(SysUser::getId, loginUserDetails.getLoginUser().getUserId())
                .set(SysUser::getLoginIp, IpUtil.getIpAddr(WebUtil.getRequest()))
                .set(SysUser::getLoginDate,new Date())
        );

        // 生成JWT
        Map<String, Object> claimsMap = new HashMap<>();
        claimsMap.put(jiaoziJwtPropertie.getUserHerderKey(), loginUserDetails.getLoginUser().getUserId());
        claimsMap.put(jiaoziJwtPropertie.getLoginIdKey(), uuid);
        claimsMap.put(jiaoziJwtPropertie.getUserHerderDateKey(), DateUtil.currentSeconds());

        JwtBuilder jwtBuilder = Jwts.builder()
                .setSubject(jiaoziJwtPropertie.getSub()) // 签发人
                .setIssuedAt(new Date()) // 签发日期
                .setClaims(claimsMap) // 自定义信息
                .setExpiration(DateUtil.offset(new Date(), DateField.SECOND,Long.valueOf(sysConfigsService.getSysConfigs(ConfigsEnum.LOGIN_TOKEN_EXPIRATION) ).intValue()) ) // 超时时间
                .signWith(SignatureAlgorithm.HS256, jiaoziJwtPropertie.getSalt()); // 加密规则
        loginLockService.loginSeccess(loginParam.getUserName());
        return R.ok(loginUserDetails.getLoginUser().getNickName()+"，欢迎登录！", jwtBuilder.compact());
    }

    /**
     * 获取部门信息
     * @return
     */
    public LuDepartmentVO getDepartment(Long departmentId){
        LuDepartmentVO luDepartmentVO=new LuDepartmentVO();

        SysDepartment sysDepartment=sysDepartmentService.getById(departmentId);
        if(sysDepartment==null||(sysDepartment.getDepartmentState()== StateEnum.STOP)){
            throw new CommonException(REnum.LOGIN_ACCOUNT_ERROR,"所属部门异常");
        }
        luDepartmentVO.setDepartmentId(departmentId.toString());
        luDepartmentVO.setDepartmentName(sysDepartment.getDepartmentName());

        return luDepartmentVO;
    }


    /**
     * 所属角色
     * @param userId
     * @return
     */
    public List<LuRoleVO> getRroles(String userId){
        MPJLambdaWrapper<SysUserOrRole> mpjLambdaWrapper=new MPJLambdaWrapper<>();
        mpjLambdaWrapper.selectAs(SysRole::getId,LuRoleVO::getRoleId);
        mpjLambdaWrapper.selectAs(SysRole::getRoleName,LuRoleVO::getRoleName);

        mpjLambdaWrapper.leftJoin(SysRole.class,SysRole::getId,SysUserOrRole::getRoleId);

        mpjLambdaWrapper.eq(SysRole::getRoleState,StateEnum.NORMAL);
        mpjLambdaWrapper.eq(SysUserOrRole::getUserId,userId);
        mpjLambdaWrapper.orderByAsc(SysRole::getRoleSort);

        List<LuRoleVO> roleVOS=sysUserOrRoleService.selectJoinList(LuRoleVO.class,mpjLambdaWrapper);
        if(roleVOS==null){
            throw new CommonException(REnum.LOGIN_ACCOUNT_ERROR,"所属角色异常");
        }
        return roleVOS;
    }

    /**
     * 超级管理员菜单
     * @return
     */
    public List<LuMenuVO> adminMenus(){
        MPJLambdaWrapper<SysMenu> mpjLambdaWrapper=new MPJLambdaWrapper<>();
        mpjLambdaWrapper.selectAs(SysMenu::getId,LuMenuVO::getMenuId);
        mpjLambdaWrapper.selectAs(SysMenu::getMenuName,LuMenuVO::getMenuName);
        mpjLambdaWrapper.selectAs(SysMenu::getMenuType,LuMenuVO::getMenuType);
        mpjLambdaWrapper.selectAs(SysMenu::getMenuIcon,LuMenuVO::getMenuIcon);
        mpjLambdaWrapper.selectAs(SysMenu::getMenuRoute,LuMenuVO::getMenuRoute);
        mpjLambdaWrapper.selectAs(SysMenu::getMenuPower,LuMenuVO::getMenuPower);
        mpjLambdaWrapper.selectAs(SysMenu::getPId,LuMenuVO::getFatherId);
        mpjLambdaWrapper.orderByAsc(SysMenu::getMenuSort);
        mpjLambdaWrapper.eq(SysMenu::getMenuState,StateEnum.NORMAL);
        List<LuMenuVO> menuVOS= sysMenuService.selectJoinList(LuMenuVO.class,mpjLambdaWrapper);
        return menus( menuVOS);
    }

    /**
     * 角色所属菜单
     * @return
     */
    public List<LuMenuVO> roleMenus(List<LuRoleVO> roleVOS){
        List<String> roleIds=roleVOS.stream().map(LuRoleVO::getRoleId).collect(Collectors.toList());
        MPJLambdaWrapper<SysRoleOrMenu> mpjLambdaWrapper=new MPJLambdaWrapper<>();
        mpjLambdaWrapper.selectAs(SysMenu::getId,LuMenuVO::getMenuId);
        mpjLambdaWrapper.selectAs(SysMenu::getMenuName,LuMenuVO::getMenuName);
        mpjLambdaWrapper.selectAs(SysMenu::getMenuType,LuMenuVO::getMenuType);
        mpjLambdaWrapper.selectAs(SysMenu::getMenuIcon,LuMenuVO::getMenuIcon);
        mpjLambdaWrapper.selectAs(SysMenu::getMenuRoute,LuMenuVO::getMenuRoute);
        mpjLambdaWrapper.selectAs(SysMenu::getMenuPower,LuMenuVO::getMenuPower);
        mpjLambdaWrapper.selectAs(SysMenu::getPId,LuMenuVO::getFatherId);

        mpjLambdaWrapper.leftJoin(SysMenu.class,SysMenu::getId,SysRoleOrMenu::getMenuId);

        mpjLambdaWrapper.in(SysRoleOrMenu::getRoleId,roleIds);
        mpjLambdaWrapper.eq(SysMenu::getMenuState,StateEnum.NORMAL);
        mpjLambdaWrapper.orderByAsc(SysMenu::getMenuSort);
        mpjLambdaWrapper.groupBy(SysRoleOrMenu::getMenuId);
        List<LuMenuVO> menuVOS= sysRoleOrMenuService.selectJoinList(LuMenuVO.class,mpjLambdaWrapper);
        return  menus(   menuVOS);
    }

    /**
     * 组装菜单上下级
     * @param menuVOS
     * @return
     */
    public List<LuMenuVO> menus(   List<LuMenuVO> menuVOS){
        if(menuVOS==null){
            throw new CommonException(REnum.LOGIN_ACCOUNT_ERROR,"菜单异常");
        }
        //获取目录
        List<LuMenuVO> directoryList=menuVOS.stream().filter(f->f.getFatherId().equals("0")).collect(Collectors.toList());
        for (LuMenuVO directory : directoryList) {
            //获取菜单
            List<LuMenuVO> menuList=menuVOS.stream().filter(f->(f.getMenuType()==MenuTypeEnum.MENU||f.getMenuType()==MenuTypeEnum.BUTTON) && f.getFatherId().equals(directory.getMenuId()) ).collect(Collectors.toList());
            if(directory.getMenuType()==MenuTypeEnum.MENU){
                for (LuMenuVO menu : menuList) {
                    //获取按钮
                    List<LuMenuVO> buttonList=menuVOS.stream().filter(f->f.getMenuType()==MenuTypeEnum.BUTTON && f.getFatherId().equals(menu.getMenuId()) ).collect(Collectors.toList());
                    menu.setChildMenus(buttonList);
                }
            }

            directory.setChildMenus(menuList);
        }
        return directoryList;
    }
    /**
     * 所属权限
     */
    public List<String> permissions(List<LuMenuVO> menuVOS){
        List<String> permissionList=new ArrayList();
        //目录不设置权限
        for (LuMenuVO directory : menuVOS) {
            for (LuMenuVO menu : directory.getChildMenus()) {//菜单遍历
                if(StrUtil.isNotBlank(menu.getMenuPower())){
                    permissionList.add(menu.getMenuPower());
                }
                if(CollUtil.isNotEmpty(menu.getChildMenus())){
                    for (LuMenuVO button : menu.getChildMenus()) {//目录遍历
                        if(StrUtil.isNotBlank(button.getMenuPower())){
                            permissionList.add(button.getMenuPower());
                        }
                    }
                }

            }
        }
        return permissionList;
    }

}
